const Base64 = require('js-base64').Base64
import JSEncrypt from 'jsencrypt'

const prototype = Object.prototype.toString

const util = {
  /**
   * 获取参数类型
   * @param val
   * @returns {string}
   */
  getProtorype: function(val) {
    return prototype.call(val)
  },
  /**
   * 是否未定义
   * @param val
   * @returns {boolean}
   */
  isUndefined: function(val) {
    return this.getProtorype(val) === '[object Undefined]'
  },
  /**
   * 是否为null
   * @param val
   * @returns {boolean}
   */
  isNull: function(val) {
    return this.getProtorype(val) === '[object Null]'
  },
  /**
   *  是否为空
   *  null -> true
   *  undefinded -> true
   *  '' -> true
   *  '  ' -> true
   *  0 -> false
   * @param val
   * @returns {boolean}
   */
  isEmpty: function(val) {
    if (this.isUndefined(val) || this.isNull(val)) {
      return true
    } else if (this.isString(val) && val.trim().length === 0) {
      return true
    } else if (this.isObj(val)) {
      return Object.keys(val).length === 0
    } else if (this.isArray(val)) {
      return val.length === 0
    }
    return false
  },
  /**
   * 是否为字符串
   * @param val
   * @returns {boolean}
   */
  isString: function(val) {
    return this.getProtorype(val) === '[object String]'
  },
  /**
   * 是否为数字
   * @param val
   * @returns {boolean}
   */
  isNumber: function(val) {
    return this.getProtorype(val) === '[object Number]'
  },
  /**
   * 是否为Boolean
   * @param val
   * @returns {boolean}
   */
  isBoolean: function(val) {
    return this.getProtorype(val) === '[object Boolean]'
  },
  /**
   * 是否为日期
   * @param val
   * @returns {boolean}
   */
  isDate: function(val) {
    return val instanceof Date
  },
  /**
   * 是否是对象
   * @param val
   * @returns {boolean}
   */
  isObj: function(val) {
    return this.getProtorype(val) === '[object Object]'
  },
  /**
   * 是否是数组
   * @param val
   * @returns {boolean}
   */
  isArray: function(val) {
    return this.getProtorype(val) === '[object Array]'
  },
  /**
   * 是否为方法
   * @param val
   * @returns {boolean}
   */
  isFunction: function(val) {
    return this.getProtorype(val) === '[object Function]'
  },
  /**
   * 是否是json字符串
   * @param val
   * @returns {boolean}
   */
  isJsonStr: function(val) {
    if (this.isString(val)) {
      try {
        JSON.parse(val)
        return true
      } catch (e) {
        // eslint-disable-next-line no-unused-vars
      }
    }
    return false
  },
  /**
   * 是否是json字符串格式的对象
   * @param val
   * @returns {boolean}
   */
  isJsonStrObj: function(val) {
    if (this.isString(val) && this.isJsonStr(val)) {
      const trimValue = val.trim()
      if (trimValue.startsWith('{') && trimValue.endsWith('}')) {
        return true
      }
    }
    return false
  },
  /**
   * 是否是json字符串格式的数组
   * @param val
   * @returns {boolean}
   */
  isJsonStrArray: function(val) {
    if (this.isString(val) && this.isJsonStr(val)) {
      const trimValue = val.trim()
      if (trimValue.startsWith('[') && trimValue.endsWith(']')) {
        return true
      }
    }
    return false
  },
  /**
   * json字符串转对象
   * @param val
   * @returns {any}
   */
  strToObj: function(val) {
    return this.toObj(val)
  },
  /**
   * json字符串转数组
   * @param val
   * @returns {Array|any}
   */
  strToArray: function(val) {
    return this.toArray(val)
  },
  /**
   * 对象或者数组转json字符串
   * @param val
   * @returns {string}
   */
  toJsonStr: function(val) {
    if (this.isObj(val) || this.isArray(val)) {
      return JSON.stringify(val)
    } else if (this.isJsonStr(val)) {
      return val
    }
    return undefined
  },
  /**
   * json字符串转对象
   * @param val
   * @returns {any}
   */
  toObj: function(val) {
    if (this.isObj(val)) {
      return val
    } else if (this.isJsonStrObj(val)) {
      return JSON.parse(val)
    }
    return {}
  },
  /**
   * 转换为数组对象
   * @param val
   * @returns {*|Array|any|Array}
   */
  toArray: function(val) {
    if (this.isArray(val)) {
      return val
    } else if (this.isJsonStrArray(val)) {
      return JSON.parse(val)
    }
    return []
  },
  toObject: function(val) {
    if (this.isArray(val) || this.isObj(val)) {
      return val
    }
    if (this.isJsonStr(val) || this.isJsonStrArray(val)) {
      return JSON.parse(val)
    }
    return undefined
  },
  /**
   * 转换为boolean
   * @param val
   * @returns {boolean|*}
   */
  toBoolean: function(val) {
    if (this.isBoolean(val)) {
      return val
    } else if (this.isString(val) && !['0', 'false', '否', ''].includes(val.trim())) {
      return true
    }
    return false
  },
  /**
   * 转换为数字
   * @param val
   * @returns {number|*}
   */
  toNumber: function(val) {
    if (this.isNumber(val)) {
      return val
    } else if (this.isString(val) && !isNaN(val)) {
      return Number.parseFloat(val)
    }
    return 0
  },
  /**
   * 返回字符串文本
   * @param val
   * @returns {*|string}
   */
  toTextString: function(val) {
    if (this.isObj(val) || this.isArray(val)) {
      return this.toJsonStr(val)
    }
    return val
  },
  /**
   * 完全复制
   * @param val
   * @returns {any}
   */
  copy: function(val) {
    if (this.isObj(val) || this.isArray(val)) {
      return JSON.parse(JSON.stringify(val))
    } else if (this.isJsonStr(val)) {
      return JSON.parse(val)
    }
    return val
  },
  /**
   * 去除字符串空格
   * @param val
   * @returns {*}
   */
  trim: function(val) {
    if (this.isString(val)) {
      return val.trim()
    }
    return val
  },
  /**
   * 数组转为字符串
   * @param list
   * @param split
   * @returns {null|*}
   */
  arrayToStr: function(list, split) {
    if (this.isArray(list)) {
      split = split || ','
      return list.join(split)
    }
    return null
  },
  /**
   * 日期格式化
   * @param date
   * @param format
   * @returns {string|*}
   */
  dateFormat: function(date, format) {
    format = format || 'yyyy-MM-dd hh:mm:ss'
    if (date !== 'Invalid Date') {
      const o = {
        'M+': date.getMonth() + 1, // month
        'd+': date.getDate(), // day
        'h+': date.getHours(), // hour
        'm+': date.getMinutes(), // minute
        's+': date.getSeconds(), // second
        'q+': Math.floor((date.getMonth() + 3) / 3), // quarter
        'S': date.getMilliseconds() // millisecond
      }
      if (/(y+)/.test(format)) {
        format = format.replace(RegExp.$1,
          (date.getFullYear() + '').substr(4 - RegExp.$1.length))
      }
      for (const k in o) {
        if (new RegExp('(' + k + ')').test(format)) {
          format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
        }
      }
      return format
    }
    return ''
  },
  /**
   * 提取${}模板类属性
   * @param template
   */
  formatProp: function(template) {
    if (this.isEmpty(template)) {
      return []
    }
    const result = []
    const res = []
    const re = /[^${]+(?=\})/g
    let r = re.exec(template)
    while (r) {
      res.push(r)
      r = re.exec(template)
    }
    if (res.length > 0) {
      for (let i = 0; i < res.length; i++) {
        const field = res[i][0]
        if (!result.includes(field)) {
          result.push(field)
        }
      }
    }
    return result
  },
  /**
   * 模板格式化
   * ${}
   * @param template
   * @param obj
   * @returns {*}
   */
  formatCode: function(template, obj) {
    if (this.isEmpty(template)) {
      return template
    }
    if (this.isObj(template)) {
      const keys = Object.keys(template)
      const newObj = {}
      keys.forEach(key => {
        newObj[key] = this.formatCode(this.callPropValue(template, key), obj)
      })
      return newObj
    }
    const res = []
    const re = /[^${]+(?=\})/g
    let r = re.exec(template)
    while (r) {
      res.push(r)
      r = re.exec(template)
    }
    if (res.length > 0) {
      for (let i = 0; i < res.length; i++) {
        const field = res[i][0]
        let value = this.callPropValue(obj, field)
        if (this.isEmpty(value)) {
          value = ''
        }
        const replaeP = '${' + field + '}'
        template = template.replace(replaeP, value)
      }
    } else if (Object.keys(obj).includes(template)) {
      template = this.callPropValue(obj, template)
    }
    return template
  },
  /**
   *  ${xxx} 占位符替换
   * @param template
   * @param obj
   * @returns {*}
   */
  formatReplace: function(template, obj) {
    const res = []
    const re = /[^${]+(?=})/g
    let r = re.exec(template)
    while (r) {
      res.push(r)
      r = re.exec(template)
    }
    if (res.length > 0) {
      for (let i = 0; i < res.length; i++) {
        const field = res[i][0]
        if (this.isEmpty(field) || !res[i].input.startsWith('${')) {
          continue
        }
        let value = this.callPropValue(obj, field)
        if (this.isEmpty(value)) {
          value = ''
        }
        const replaeP = '${' + field + '}'
        template = template.replace(replaeP, value)
      }
      return template
    }
    return template
  },
  /**
   * 获取对象下值
   * @param model
   * @param prop
   */
  callPropValue: function(model, prop) {
    try {
      const execArgs = ['model']
      const execParams = [model]
      if (this.isArray(model)) {
        execArgs.push('return model' + prop)
      } else {
        execArgs.push('return model.' + prop)
      }
      const customFun = Function.apply(null, execArgs)
      return customFun.apply(null, execParams)
    } catch (e) {
      console.error('属性获取错误:', model, prop)
    }
    return undefined
  },
  /**
   * 生成uuid
   * @param len
   * @param radix
   * @returns {string}
   */
  uuid: function(len, radix) {
    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
    const uuid = []
    let i
    radix = radix || chars.length
    len = len || 32
    for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
    return uuid.join('')
  },
  /**
   * 对象属性复制赋值
   * @param source
   * @param target
   * @param ignores []
   * @param e
   * @param ignoresVal 目标对象有值时，忽略设置
   */
  copyAttr: function(source, target, ignores, e, ignoresVal) {
    if (this.isObj(source) && this.isObj(target)) {
      const ignoreList = this.toArray(ignores)
      const keys = Object.keys(source)
      const copyAttrs = keys.filter(key => !ignoreList.includes(key))
      copyAttrs.forEach(key => {
        const tarVal = this.callPropValue(target, key)
        if (this.isEmpty(tarVal) || !ignoresVal || (this.isNumber(tarVal) && tarVal === 0)) {
          if (e) {
            e.$set(target, key, source[key])
          } else {
            target[key] = source[key]
          }
        }
      })
    }
  },
  /**
   * 模板复制
   * @param source
   * @param target
   */
  copyOnly: function(source, target) {
    if (this.isObj(source) && this.isObj(target)) {
      const keys = Object.keys(target)
      keys.forEach(key => {
        const value = this.callPropValue(source, key)
        if (!this.isEmpty(value)) {
          target[key] = value
        }
      })
    }
  },
  /**
   * 深度对象合并
   * source 内的值替换 target的值
   * @param source  合并对象
   * @param target  被合并对象
   * @param ignores []
   */
  deepMerge: function(source, target, ignores) {
    if (!this.isObj(target) || !this.isObj(source)) {
      return target
    }
    let keys = Object.keys(source)
    ignores = this.isArray(ignores) ? ignores : []
    if (ignores.length > 0) {
      keys = keys.filter(item => !ignores.includes(item))
    }
    const newObj = {}
    keys.forEach(key => {
      const value = source[key]
      if (this.isEmpty(target[key])) {
        newObj[key] = this.copy(value)
      } else if (this.isObj(value)) {
        newObj[key] = this.deepMerge(value, target[key], ignores)
      } else if (this.isArray(value)) {
        const olds = this.toArray(target[key])
        for (let i = 0; i < value.length; i++) {
          const child = value[i]
          if (this.isObj(child)) {
            newObj[key].push(this.deepMerge(child, olds[i]), ignores)
          }
        }
      }
    })
    return newObj
  },
  /**
   * 请求响应检查转换
   * @param res
   * @param code
   * @param msg
   * @returns {{msg: *, code: (*|number)}}
   */
  responseCheck: function(res, code, msg) {
    if (this.isEmpty(res)) {
      return { code: code || 200, msg: msg }
    }
    const keys = Object.keys(res)
    if (keys.includes('code')) {
      return res
    }
    return { code: code || 200, msg: msg, data: res }
  },
  /**
   * 地址拼接
   * @param host
   * @param url
   * @returns {string|*}
   */
  mergeUrl: function(host, url) {
    if (this.isEmpty(host)) {
      return url
    }
    if (url.startsWith('http://') || url.startsWith('https://')) {
      return url
    }
    if (url.startsWith('/')) {
      url = url.substring(1)
    }
    if (host.endsWith('/')) {
      return host + url
    }
    return host + '/' + url
  },
  /**
   * 对象清空
   * @param obj
   * @param ignores
   */
  cleanObj: function(e, obj, ignores) {
    if (this.isEmpty(obj)) {
      return
    }
    if (this.isArray(obj)) {
      obj.splice(0, obj.length)
    }
    if (!this.isObj(obj)) {
      return
    }
    let keys = Object.keys(obj)
    if (!this.isEmpty(ignores)) {
      keys = keys.filter(item => !ignores.includes(item))
    }
    keys.forEach(key => {
      e.$delete(obj, key)
    })
  },
  /**
   * base64编码
   * @param str  字符串
   * @param urlsafe  字符串
   * @returns {string}
   */
  base64Encode: function(str, urlsafe) {
    return Base64.encode(str, urlsafe)
  },
  /**
   * base64解码
   * @param str  字符串
   * @returns {string}
   */
  base64Decode: function(str) {
    return Base64.decode(str)
  },
  /**
   * 公钥加密
   * @param pub
   * @param str
   * @returns {*}
   */
  pubEncode(pub, str) {
    const jsencrypt = new JSEncrypt()
    jsencrypt.setPublicKey(pub)
    return jsencrypt.encrypt(str)
  },
  /**
   * 公钥解码
   * @param pub
   * @param str
   * @returns {*}
   */
  pubDecode(pub, str) {
    const jsencrypt = new JSEncrypt()
    return jsencrypt.decrypt(str)
  }
}

export default util
